import { Vector2, Vector2tuple, Vector3 } from 'three-legacy';

import { CSDashLine, EventsRes } from '@amcax/base';
import {
  calcLineLength,
  calculatePoint,
  getDotProduct,
  getLineIntersectionTwo,
  isParallel,
  pointProjectOnLine,
  renderSketch,
  updataRenderDim,
} from '@app-cad/common/utils';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { ctx2 } from '@app-cad/module';
import { wsApi } from '@app-cad/server';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase from '../../CmdBase';
import { DimAngleTool } from '../dimension/DimAngleTool';
import { DimLinear } from '../dimension/DimLinear';
import { MouseState, State } from './types';

export class CmdInteractor extends CmdBase {
  curves: Vector3[][] = new Array(2);
  curveIds: string[] = new Array(2);
  intersection: Vector3; // 交点
  dimLinear: DimLinear;
  lineVecs: Vector3[] = new Array(2); // 线向量
  previewLine: CSDashLine; // 预览线
  chamferLineLength: Vector2tuple;
  is_parallel: boolean = false;
  private mouseDownCount = 0;
  // 鼠标移动倒角线端点
  points = new Array(2);
  private currentState: MouseState = State.NONE;
  registerEvents() {
    sketchEventCenter()
      .setState('cmdSketchChamfer')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .on(SKETCH_CENTEREVENTS.POINTERUP, this.onMouseUp)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  }
  unregisterEvents() {
    sketchEventCenter()
      .setDefault()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERDOWN, this.onMouseDown)
      .off(SKETCH_CENTEREVENTS.POINTERUP, this.onMouseUp)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp);
  }
  constructor() {
    super();
    this.registerEvents();
  }
  onMouseMove = (e: EventsRes) => {
    this.setMouseWV(e, false, false); // 设置鼠标位置
    if (this.currentState === State.DRAW) {
      this.preview();
    }
  };
  onMouseDown = (e: EventsRes) => {
    if (e.button.onLeft) {
      const info = this.picker.pick(e, { line: true });
      // 前两次点击必须要pick到直线才开始计数，否则不计数
      if (this.mouseDownCount < 2 && info?.type === 'Line') {
        this.mouseDownCount += 1;
      }

      if (this.currentState < State.DRAW && info?.type === 'Line') {
        this.curves[this.currentState] = info.vertices.map((v) => {
          return new Vector3(v[0], v[1], 0);
        });
        this.curveIds[this.currentState] = info.id;
        this.currentState += 1;
      }
      if (this.currentState === State.DRAW) {
        this.curves.forEach((curve, index) => {
          this.lineVecs[index] = new Vector3()
            .subVectors(curve[1], curve[0])
            .normalize();
        });
        // 判断是否相交
        const corssInfo = getLineIntersectionTwo(
          this.curves[0][1],
          this.curves[0][0],
          this.curves[1][1],
          this.curves[1][0],
        );
        this.is_parallel = isParallel(this.lineVecs[0], this.lineVecs[1]);
        if (!this.is_parallel) {
          this.intersection = new Vector3(corssInfo.x, corssInfo.y, 0);
        }
      }
    } else if (e.button.onRight) {
      // 鼠标右键，取消绘制
      this.reset();
      this.releaseObjects();
    }
  };

  isLoading = false;
  onMouseUp = async () => {
    if (this.isLoading) {
      return;
    }

    if (this.previewLine && this.mouseDownCount === 2) {
      //  起始点
      const m1: Vector2 = new Vector2(this.points[0].x, this.points[0].y);
      // 终止点
      const m2: Vector2 = new Vector2(this.points[1].x, this.points[1].y);
      let dim: DimAngleTool;
      if (!this.is_parallel) {
        // 交点
        const intersection: Vector2 = new Vector2(
          this.intersection.x,
          this.intersection.y,
        );

        const length1 = calcLineLength(m1, intersection);
        const length2 = calcLineLength(m2, intersection);

        this.isLoading = true;

        try {
          const result = await wsApi.chamferSketcherCurve(
            this.sketchId,
            this.curveIds,
            false,
            [m1.x, m1.y],
            [m2.x, m2.y],
            length1,
            length2,
          );
          await renderSketch(this.sketchId, result);
          await updataRenderDim(this.sketchId);
        } finally {
          this.isLoading = false;
        }
      } else if (this.is_parallel) {
        MessageTip.error('平行线不支持倒角');
      }

      dim?.destroy();
      this.reset();

      this.releaseObjects();
      this.exitFunc?.();
    }
  };
  preview() {
    if (this.currentState === State.DRAW) {
      const points = new Array(2);
      let flag = true;
      if (!this.is_parallel) {
        const l = this.mouseWV.distanceTo(this.intersection);
        this.lineVecs.forEach((vec, index) => {
          const symbol =
            getDotProduct(this.intersection, this.mouseWV, vec) >= 0 ? 1 : -1;
          points[index] = calculatePoint(vec, l * symbol, this.intersection);
          const flag_temp = inLine(points[index], this.curves[index]);
          if (!flag_temp) flag = false; // 判断是否在垂直范围内
        });
      } else {
        this.lineVecs.forEach((vec, index) => {
          points[index] = pointProjectOnLine(
            this.mouseWV,
            this.curves[index][0],
            this.lineVecs[index],
          );

          const flag_temp = inLine(points[index], this.curves[index]);
          if (!flag_temp) flag = false; // 判断是否在垂直范围内
        });
      }
      if (!this.previewLine) {
        this.previewLine = new CSDashLine({
          vertexs: points.map((v) => this.sketchFrame.toWorld(v)),
          lineWidth: 2,
        });
        this.previewLine.addTo(this.assisGroup);
      } else if (flag) {
        this.previewLine.update({
          vertexs: points.map((v) => this.sketchFrame.toWorld(v)),
        });
      }

      this.points = points;
      ctx2.viewer.requestRender();
    }
  }
  reset() {
    this.currentState = State.NONE;
    this.curves = new Array(2);
    this.curveIds = new Array(2);
    this.intersection = null;
    this.lineVecs = new Array(2);
    this.previewLine = null;
    this.mouseDownCount = 0;
  }
  releaseObjects() {
    this.previewLine && this.previewLine.destroy();
    this.assisGroup.clear();
    ctx2.viewer.requestRender();
  }
}

const inLine = (point: Vector3, line: Vector3[]) => {
  const l1 = point.distanceToSquared(line[0]);
  const l2 = point.distanceToSquared(line[1]);
  const l3 = line[0].distanceToSquared(line[1]);
  return l3 >= l1 + l2;
};
